/******************************************************************************* * Copyright (c) 2007, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Remy Chi Jian Suen <remy.suen@gmail.com> - bug 201661 * Simon Scholz <simon.scholz@vogella.com> - Bug 483425, 483429, 483435 *******************************************************************************/ package org.eclipse.ui.dialogs; import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter; import com.ibm.icu.text.Collator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkingSet; import org.eclipse.ui.IWorkingSetManager; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.internal.IWorkbenchHelpContextIds; import org.eclipse.ui.internal.WorkbenchMessages; import org.eclipse.ui.internal.WorkbenchPlugin; import org.eclipse.ui.internal.dialogs.SimpleWorkingSetSelectionDialog; /** * Instances of this class provide a reusable composite with controls that allow * the selection of working sets. This class is most useful in * {@link IWizardPage} instances that wish to create resources and pre-install * them into particular working sets. * * @since 3.4 * */ public class WorkingSetConfigurationBlock { /** * Filters the given working sets such that the following is true: for each * IWorkingSet s in result: s.getId() is element of workingSetIds * * @param workingSets * the array to filter * @param workingSetIds * the acceptable working set ids * @return the filtered elements */ public static IWorkingSet[] filter(IWorkingSet[] workingSets, String[] workingSetIds) { // create a copy so we can sort the array without mucking it up for clients. String[] workingSetIdsCopy = new String[workingSetIds.length]; System.arraycopy(workingSetIds, 0, workingSetIdsCopy, 0, workingSetIds.length); Arrays.sort(workingSetIdsCopy); List<IWorkingSet> result = new ArrayList<>(); for (IWorkingSet workingSet : workingSets) { if (Arrays.binarySearch(workingSetIdsCopy, workingSet.getId()) >= 0) { result.add(workingSet); } } return result.toArray(new IWorkingSet[result.size()]); } /** * Empty working set array constant. */ private static final IWorkingSet[] EMPTY_WORKING_SET_ARRAY = new IWorkingSet[0]; private static final String WORKINGSET_SELECTION_HISTORY = "workingset_selection_history"; //$NON-NLS-1$ private static final int MAX_HISTORY_SIZE = 5; private Label workingSetLabel; private Combo workingSetCombo; private Button selectButton; private Button enableButton; private Button newButton; private IWorkingSet[] selectedWorkingSets; private List<String> selectionHistory; private final IDialogSettings dialogSettings; private final String[] workingSetTypeIds; private final String selectLabel; private final String comboLabel; private final String enableButtonLabel; private final String newButtonLabel; /** * Create a new instance of this working set block using default labels. * * @param settings * to store/load the selection history * @param workingSetIds * working set ids from which the user can choose * @since 3.108 */ public WorkingSetConfigurationBlock(IDialogSettings settings, String... workingSetIds) { this(settings, null, null, null, null, workingSetIds); } /** * Create a new instance of this working set block using default labels. * <br/> * <br/> * Note: Consider using the vararg version of this contructor. * * @param workingSetIds * working set ids from which the user can choose * @param settings * to store/load the selection history */ public WorkingSetConfigurationBlock(String[] workingSetIds, IDialogSettings settings) { this(settings, workingSetIds); } /** * Create a new instance of this working set block using custom labels. * <br/> * <br/> * Note: Consider using the vararg version of this contructor. * * @param workingSetIds * working set ids from which the user can choose * @param settings * to store/load the selection history * @param enableButtonLabel * the label to use for the checkable enablement button. May be * <code>null</code> to use the default value. * @param comboLabel * the label to use for the recent working set combo. May be * <code>null</code> to use the default value. * @param selectLabel * the label to use for the select button. May be * <code>null</code> to use the default value. */ public WorkingSetConfigurationBlock(String[] workingSetIds, IDialogSettings settings, String enableButtonLabel, String comboLabel, String selectLabel) { this(settings, enableButtonLabel, null, comboLabel, selectLabel, workingSetIds); } /** * Create a new instance of this working set block using custom labels. * * @param settings * to store/load the selection history * @param enableButtonLabel * the label to use for the checkable enablement button. May be * <code>null</code> to use the default value. * @param newButtonLabel * the label to use for the new button. May be <code>null</code> * to use the default value. * @param comboLabel * the label to use for the recent working set combo. May be * <code>null</code> to use the default value. * @param selectLabel * the label to use for the select button. May be * <code>null</code> to use the default value. * @param workingSetIds * working set ids from which the user can choose * @since 3.108 */ public WorkingSetConfigurationBlock(IDialogSettings settings, String enableButtonLabel, String newButtonLabel, String comboLabel, String selectLabel, String... workingSetIds) { Assert.isNotNull(workingSetIds); Assert.isNotNull(settings); workingSetTypeIds = workingSetIds; Arrays.sort(workingSetIds); // we'll be performing some searches with these later - presort them selectedWorkingSets = EMPTY_WORKING_SET_ARRAY; dialogSettings = settings; selectionHistory = loadSelectionHistory(settings, workingSetIds); this.enableButtonLabel = enableButtonLabel == null ? WorkbenchMessages.WorkingSetGroup_EnableWorkingSet_button : enableButtonLabel; this.newButtonLabel = newButtonLabel == null ? WorkbenchMessages.WorkingSetConfigurationBlock_NewWorkingSet_button : newButtonLabel; this.comboLabel = comboLabel == null ? WorkbenchMessages.WorkingSetConfigurationBlock_WorkingSetText_name : comboLabel; this.selectLabel = selectLabel == null ? WorkbenchMessages.WorkingSetConfigurationBlock_SelectWorkingSet_button : selectLabel; } /** * Set the current selection in the workbench. * * @param selection * the selection to present in the UI or <b>null</b> * @deprecated use * {@link #setWorkingSets(IWorkingSet[])} and {@link #findApplicableWorkingSets(IStructuredSelection)} * instead. */ @Deprecated public void setSelection(IStructuredSelection selection) { selectedWorkingSets = findApplicableWorkingSets(selection); if (workingSetCombo != null) { updateSelectedWorkingSets(); } } /** * Set the current selection of working sets. This array will be filtered to * contain only working sets that are applicable to this instance. * * @param workingSets * the working sets */ public void setWorkingSets(IWorkingSet... workingSets) { selectedWorkingSets = filterWorkingSets(Arrays.asList(workingSets)); if (workingSetCombo != null) { updateSelectedWorkingSets(); } } /** * Retrieves a working set from the given <code>selection</code> or an * empty array if no working set could be retrieved. This selection is * filtered based on the criteria used to construct this instance. * * @param selection * the selection to retrieve the working set from * @return the selected working set or an empty array */ public IWorkingSet[] findApplicableWorkingSets( IStructuredSelection selection) { if (selection == null) { return EMPTY_WORKING_SET_ARRAY; } return filterWorkingSets(selection.toList()); } /** * Prune a list of working sets such that they all match the criteria set * out by this block. * * @param elements * the elements to filter * @return the filtered elements */ private IWorkingSet[] filterWorkingSets(Collection<?> elements) { List<IWorkingSet> result = new ArrayList<>(); for (Object element : elements) { if (element instanceof IWorkingSet && verifyWorkingSet((IWorkingSet) element)) { result.add((IWorkingSet) element); } } return result.toArray(new IWorkingSet[result.size()]); } /** * Verifies that the given working set is suitable for selection in this * block. * * @param workingSetCandidate * the candidate to test * @return whether it is suitable */ private boolean verifyWorkingSet(IWorkingSet workingSetCandidate) { return !workingSetCandidate.isAggregateWorkingSet() && Arrays.binarySearch(workingSetTypeIds, workingSetCandidate .getId()) >= 0 ; } /** * Return the currently selected working sets. If the controls representing * this block are disabled this array will be empty regardless of the * currently selected values. * * @return the selected working sets */ public IWorkingSet[] getSelectedWorkingSets() { if (enableButton.getSelection()) { return selectedWorkingSets; } return EMPTY_WORKING_SET_ARRAY; } /** * Add this block to the <code>parent</parent> * * @param parent the parent to add the block to */ public void createContent(final Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); composite.setLayout(new GridLayout(3, false)); enableButton = new Button(composite, SWT.CHECK); enableButton .setText(enableButtonLabel); GridData enableData = new GridData(SWT.FILL, SWT.CENTER, true, false); enableData.horizontalSpan = 2; enableButton.setLayoutData(enableData); enableButton.setSelection(selectedWorkingSets.length > 0); newButton = new Button(composite, SWT.PUSH); newButton.setText(this.newButtonLabel); setButtonLayoutData(newButton); newButton.addSelectionListener(widgetSelectedAdapter(e -> createNewWorkingSet(newButton.getShell()))); workingSetLabel = new Label(composite, SWT.NONE); workingSetLabel .setText(comboLabel); workingSetCombo = new Combo(composite, SWT.READ_ONLY | SWT.BORDER); GridData textData = new GridData(SWT.FILL, SWT.CENTER, true, false); textData.horizontalIndent = 0; workingSetCombo.setLayoutData(textData); selectButton = new Button(composite, SWT.PUSH); selectButton .setText(selectLabel); setButtonLayoutData(selectButton); selectButton.addSelectionListener(widgetSelectedAdapter(e -> { SimpleWorkingSetSelectionDialog dialog = new SimpleWorkingSetSelectionDialog(parent.getShell(), workingSetTypeIds, selectedWorkingSets, false); dialog.setMessage(WorkbenchMessages.WorkingSetGroup_WorkingSetSelection_message); if (dialog.open() == Window.OK) { IWorkingSet[] result = dialog.getSelection(); if (result != null && result.length > 0) { selectedWorkingSets = result; PlatformUI.getWorkbench().getWorkingSetManager().addRecentWorkingSet(result[0]); } else { selectedWorkingSets = EMPTY_WORKING_SET_ARRAY; } updateWorkingSetSelection(); } })); enableButton.addSelectionListener(widgetSelectedAdapter(e -> updateEnableState(enableButton.getSelection()))); updateEnableState(enableButton.getSelection()); workingSetCombo.addSelectionListener(widgetSelectedAdapter(e -> updateSelectedWorkingSets())); workingSetCombo.setItems(getHistoryEntries()); if (selectedWorkingSets.length == 0 && selectionHistory.size() > 0) { workingSetCombo.select(historyIndex(selectionHistory.get(0))); updateSelectedWorkingSets(); } else { updateWorkingSetSelection(); } } private void createNewWorkingSet(Shell shell) { IWorkingSetManager manager = WorkbenchPlugin.getDefault().getWorkingSetManager(); IWorkingSetNewWizard wizard = manager.createWorkingSetNewWizard(workingSetTypeIds); // the wizard can never be null since we have at least a resource // working set creation page WizardDialog dialog = new WizardDialog(shell, wizard); dialog.create(); PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(), IWorkbenchHelpContextIds.WORKING_SET_NEW_WIZARD); if (dialog.open() == Window.OK) { IWorkingSet workingSet = wizard.getSelection(); if (workingSet != null) { manager.addWorkingSet(workingSet); selectedWorkingSets = new IWorkingSet[] { workingSet }; PlatformUI.getWorkbench().getWorkingSetManager().addRecentWorkingSet(workingSet); } enableButton.setSelection(true); updateEnableState(true); updateWorkingSetSelection(); } } private void updateEnableState(boolean enabled) { workingSetLabel.setEnabled(enabled); workingSetCombo .setEnabled(enabled && (selectedWorkingSets.length > 0 || getHistoryEntries().length > 0)); selectButton.setEnabled(enabled); } private void updateWorkingSetSelection() { if (selectedWorkingSets.length > 0) { workingSetCombo.setEnabled(true); StringBuffer buf = new StringBuffer(); buf.append(selectedWorkingSets[0].getLabel()); for (int i = 1; i < selectedWorkingSets.length; i++) { IWorkingSet ws = selectedWorkingSets[i]; buf.append(',').append(' '); buf.append(ws.getLabel()); } String currentSelection = buf.toString(); int index = historyIndex(currentSelection); historyInsert(currentSelection); if (index >= 0) { workingSetCombo.select(index); } else { workingSetCombo.setItems(getHistoryEntries()); workingSetCombo.select(historyIndex(currentSelection)); } } else { enableButton.setSelection(false); updateEnableState(false); } } private String[] getHistoryEntries() { String[] history = selectionHistory .toArray(new String[selectionHistory.size()]); Arrays.sort(history, (o1, o2) -> Collator.getInstance().compare(o1, o2)); return history; } private void historyInsert(String entry) { selectionHistory.remove(entry); selectionHistory.add(0, entry); storeSelectionHistory(dialogSettings); } private int historyIndex(String entry) { for (int i = 0; i < workingSetCombo.getItemCount(); i++) { if (workingSetCombo.getItem(i).equals(entry)) { return i; } } return -1; } // copied from org.eclipse.jdt.internal.ui.text.JavaCommentScanner private String[] split(String value, String delimiters) { StringTokenizer tokenizer= new StringTokenizer(value, delimiters); int size= tokenizer.countTokens(); String[] tokens= new String[size]; int i= 0; while (i < size) { tokens[i++]= tokenizer.nextToken(); } return tokens; } private void updateSelectedWorkingSets() { String item = workingSetCombo.getItem(workingSetCombo .getSelectionIndex()); String[] workingSetNames = split(item, ", "); //$NON-NLS-1$ IWorkingSetManager workingSetManager = PlatformUI.getWorkbench() .getWorkingSetManager(); selectedWorkingSets = new IWorkingSet[workingSetNames.length]; for (int i = 0; i < workingSetNames.length; i++) { IWorkingSet set = workingSetManager .getWorkingSet(workingSetNames[i]); Assert.isNotNull(set); selectedWorkingSets[i] = set; } } private void storeSelectionHistory(IDialogSettings settings) { String[] history; if (selectionHistory.size() > MAX_HISTORY_SIZE) { List<String> subList = selectionHistory.subList(0, MAX_HISTORY_SIZE); history = subList.toArray(new String[subList.size()]); } else { history = selectionHistory .toArray(new String[selectionHistory.size()]); } settings.put(WORKINGSET_SELECTION_HISTORY, history); } private List<String> loadSelectionHistory(IDialogSettings settings, String... workingSetIds) { String[] strings = settings.getArray(WORKINGSET_SELECTION_HISTORY); if (strings == null || strings.length == 0) { return new ArrayList<>(); } List<String> result = new ArrayList<>(); Set<String> workingSetIdsSet = new HashSet<>(Arrays.asList(workingSetIds)); IWorkingSetManager workingSetManager = PlatformUI.getWorkbench() .getWorkingSetManager(); for (String string : strings) { String[] workingSetNames = split(string, ", "); //$NON-NLS-1$ boolean valid = true; for (int j = 0; j < workingSetNames.length && valid; j++) { IWorkingSet workingSet = workingSetManager .getWorkingSet(workingSetNames[j]); if (workingSet == null) { valid = false; } else if (!workingSetIdsSet.contains(workingSet.getId())) { valid = false; } } if (valid) { result.add(string); } } return result; } /* * Copy from DialogPage with changes to accomodate the lack of a Dialog context. */ private GridData setButtonLayoutData(Button button) { button.setFont(JFaceResources.getDialogFont()); GC gc = new GC(button); gc.setFont(button.getFont()); FontMetrics fontMetrics = gc.getFontMetrics(); gc.dispose(); GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); int widthHint = Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.BUTTON_WIDTH); Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); data.widthHint = Math.max(widthHint, minSize.x); button.setLayoutData(data); return data; } }